/*
 *  Copyright 2005 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.service.core.impl;

import com.blandware.atleap.common.Constants;
import com.blandware.atleap.common.util.ConvertUtil;
import com.blandware.atleap.common.util.ObjectCountInfo;
import com.blandware.atleap.common.util.PartialCollection;
import com.blandware.atleap.common.util.QueryInfo;
import com.blandware.atleap.model.core.ContentResource;
import com.blandware.atleap.model.core.Page;
import com.blandware.atleap.model.core.User;
import com.blandware.atleap.model.core.statistics.*;
import com.blandware.atleap.persistence.core.ContentResourceDAO;
import com.blandware.atleap.persistence.core.PageDAO;
import com.blandware.atleap.persistence.core.StatisticsDAO;
import com.blandware.atleap.persistence.core.UserDAO;
import com.blandware.atleap.service.core.StatisticsManager;
import com.blandware.atleap.service.core.util.RawData;
import com.blandware.atleap.service.core.util.VisitorSoftwareUtil;
import org.apache.commons.validator.GenericValidator;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

/**
 * <p>Implementation of StatisticsManager</p>
 * <p><a href="StatisticsManagerImpl.java.html"><i>View source</i></a></p>
 *
 * @author Roman Puchkovskiy <a href="mailto:roman.puchkovskiy@blandware.com">
 *         &lt;roman.puchkovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.12 $ $Date: 2007/12/24 08:54:24 $
 */
public class StatisticsManagerImpl extends BaseManagerImpl
        implements StatisticsManager {

    /**
     * VisitorSoftwareUtil instance
     */
    protected VisitorSoftwareUtil visitorSoftwareUtil = null;

    /**
     * DAO to work with statistics
     */
    protected StatisticsDAO statisticsDAO = null;

    /**
     * DAO to work with users
     */
    protected UserDAO userDAO = null;

    /**
     * DAO to work with pages
     */
    protected PageDAO pageDAO = null;

    /**
     * DAO to work with content resources
     */
    protected ContentResourceDAO contentResourceDAO = null;

    /**
     * Contains a session number that will be returned next time
     */
    protected Integer nextSessionNumber = new Integer(0);

    /**
     * Sets VisitorSoftwareUtil instance.
     *
     * @param visitorSoftwareUtil instance to set
     */
    public void setVisitorSoftwareUtil(VisitorSoftwareUtil visitorSoftwareUtil) {
        this.visitorSoftwareUtil = visitorSoftwareUtil;
    }

    /**
     * Sets DAO to work with statistics.
     *
     * @param statisticsDAO DAO to set
     */
    public void setStatisticsDAO(StatisticsDAO statisticsDAO) {
        this.statisticsDAO = statisticsDAO;
    }

    /**
     * Sets DAO to work with users.
     *
     * @param userDAO DAO to set
     */
    public void setUserDAO(UserDAO userDAO) {
        this.userDAO = userDAO;
    }

    /**
     * Sets DAO to work with pages.
     *
     * @param pageDAO DAO to set
     */
    public void setPageDAO(PageDAO pageDAO) {
        this.pageDAO = pageDAO;
    }

    /**
     * Sets DAO to work with content resources.
     *
     * @param contentResourceDAO DAO to set
     */
    public void setContentResourceDAO(ContentResourceDAO contentResourceDAO) {
        this.contentResourceDAO = contentResourceDAO;
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#getFirstFreeSessionNumber()
     */
    public Integer getFirstFreeSessionNumber() {
        return statisticsDAO.getFirstFreeSessionNumber();
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#getFirstFreeVisitorNumber()
     */
    public Integer getFirstFreeVisitorNumber() {
        return statisticsDAO.getFirstFreeVisitorNumber();
    }

    /**
     * Returns the least of numbers that were not ever used as a session number.
     *
     * @return first free session number
     */
    public Integer getNextFreeSessionNumber() {
        synchronized (this) {
            Integer result = nextSessionNumber;
            nextSessionNumber = new Integer(nextSessionNumber.intValue() + 1);
            return result;
        }
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#initialize()
     */
    public void initialize() {
        synchronized (this) {
            nextSessionNumber = statisticsDAO.getFirstFreeSessionNumber();
        }
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#processRawData(java.util.List,boolean,boolean,Integer)
     */
    public Integer processRawData(List rawDataList, boolean containsEntryPoint,
                                  boolean containsExitPoint,
                                  Integer sessionNumber) {
        // setting session number if needed
        Integer sessNumber = sessionNumber;
        if (sessNumber == null) {
            sessNumber = getNextFreeSessionNumber();
        }

        // looking for last NON-RESOURCE
        int index;
        for (index = rawDataList.size() - 1; index >= 0; index--) {
            RawData data = (RawData) rawDataList.get(index);
            if (data.isNotResource()) {
                break;
            }
        }
        boolean onlyResources = index < 0;
        boolean entryPointIsMet = false;
        for (Iterator i = rawDataList.iterator(); i.hasNext(); ) {
            RawData data = (RawData) i.next();
            boolean isEntryPoint = data.isNotResource() && !entryPointIsMet && containsEntryPoint;
            boolean isExitPoint = containsExitPoint && !onlyResources
                    && rawDataList.get(index) == data;
            if (!isEntryPoint) {
                data.setReferrerUrl(null);
                data.setReferrerDomain(null);
                data.setReferrerQueryParameters(new HashMap());
            }
            Visit visit = convertRawDataToVisit(data);
            visit.setSessionNumber(sessNumber);
            visit.setEntryPoint((isEntryPoint) ? Boolean.TRUE : Boolean.FALSE);
            visit.setExitPoint((isExitPoint) ? Boolean.TRUE : Boolean.FALSE);
            statisticsDAO.createVisit(visit);
            if (isEntryPoint) {
                entryPointIsMet = true;
            }
        }

        return sessNumber;
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#listTopBrowsers(com.blandware.atleap.common.util.QueryInfo)
     */
    public PartialCollection listTopBrowsers(QueryInfo queryInfo) {
        PartialCollection triads = statisticsDAO.listTopBrowsers(queryInfo);
        List result = new ArrayList(triads.size());
        for (Iterator i = triads.iterator(); i.hasNext(); ) {
            Object[] triad = (Object[]) i.next();
            Integer browserId = (Integer) triad[0];
            Integer total = (Integer) triad[1];
            Integer unique = (Integer) triad[2];
            BrowserDescription description = visitorSoftwareUtil.getBrowserDescription(browserId);
            UserAgent userAgent = new UserAgent(description);
            result.add(new ObjectCountInfo(userAgent, total, unique));
        }
        return new PartialCollection(result, triads.getTotal());
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#listTopOSes(com.blandware.atleap.common.util.QueryInfo)
     */
    public PartialCollection listTopOSes(QueryInfo queryInfo) {
        PartialCollection triads = statisticsDAO.listTopOSes(queryInfo);
        List result = new ArrayList(triads.size());
        for (Iterator i = triads.iterator(); i.hasNext(); ) {
            Object[] triad = (Object[]) i.next();
            Integer osId = (Integer) triad[0];
            Integer total = (Integer) triad[1];
            Integer unique = (Integer) triad[2];
            OSDescription description = visitorSoftwareUtil.getOSDescription(osId);
            UserAgent userAgent = new UserAgent(description);
            result.add(new ObjectCountInfo(userAgent, total, unique));
        }
        return new PartialCollection(result, triads.getTotal());
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#listTopRobots(com.blandware.atleap.common.util.QueryInfo)
     */
    public PartialCollection listTopRobots(QueryInfo queryInfo) {
        PartialCollection triads = statisticsDAO.listTopRobots(queryInfo);
        List result = new ArrayList(triads.size());
        for (Iterator i = triads.iterator(); i.hasNext(); ) {
            Object[] triad = (Object[]) i.next();
            Integer robotId = (Integer) triad[0];
            Integer total = (Integer) triad[1];
            Integer unique = (Integer) triad[2];
            RobotDescription description = visitorSoftwareUtil.getRobotDescription(robotId);
            UserAgent userAgent = new UserAgent(description);
            result.add(new ObjectCountInfo(userAgent, total, unique));
        }
        return new PartialCollection(result, triads.getTotal());
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#listTopPages(com.blandware.atleap.common.util.QueryInfo)
     */
    public PartialCollection listTopPages(QueryInfo queryInfo) {
        PartialCollection triads = statisticsDAO.listTopPages(queryInfo);
        List result = new ArrayList(triads.size());
        for (Iterator i = triads.iterator(); i.hasNext(); ) {
            Object[] triad = (Object[]) i.next();
            Page page = (Page) triad[0];
            Integer total = (Integer) triad[1];
            Integer unique = (Integer) triad[2];
            result.add(new ObjectCountInfo(page, total, unique));
        }
        return new PartialCollection(result, triads.getTotal());
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#listTopPages(com.blandware.atleap.common.util.QueryInfo)
     */
    public PartialCollection listTopContentResources(QueryInfo queryInfo) {
        PartialCollection triads = statisticsDAO.listTopContentResources(queryInfo);
        List result = new ArrayList(triads.size());
        for (Iterator i = triads.iterator(); i.hasNext(); ) {
            Object[] triad = (Object[]) i.next();
            ContentResource contentResource = (ContentResource) triad[0];
            Integer total = (Integer) triad[1];
            Integer unique = (Integer) triad[2];
            result.add(new ObjectCountInfo(contentResource, total, unique));
        }
        return new PartialCollection(result, triads.getTotal());
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#listTopReferrerUrls(com.blandware.atleap.common.util.QueryInfo)
     */
    public PartialCollection listTopReferrerUrls(QueryInfo queryInfo) {
        PartialCollection triads = statisticsDAO.listTopReferrerUrls(queryInfo);
        List result = new ArrayList(triads.size());
        for (Iterator i = triads.iterator(); i.hasNext(); ) {
            Object[] triad = (Object[]) i.next();
            String referrerUrl = (String) triad[0];
            Integer total = (Integer) triad[1];
            Integer unique = (Integer) triad[2];
            result.add(new ObjectCountInfo(referrerUrl, total, unique));
        }
        return new PartialCollection(result, triads.getTotal());
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#listTopReferrerDomains(com.blandware.atleap.common.util.QueryInfo)
     */
    public PartialCollection listTopReferrerDomains(QueryInfo queryInfo) {
        PartialCollection triads = statisticsDAO.listTopReferrerDomains(queryInfo);
        List result = new ArrayList(triads.size());
        for (Iterator i = triads.iterator(); i.hasNext(); ) {
            Object[] triad = (Object[]) i.next();
            String referrerDomain = (String) triad[0];
            Integer total = (Integer) triad[1];
            Integer unique = (Integer) triad[2];
            result.add(new ObjectCountInfo(referrerDomain, total, unique));
        }
        return new PartialCollection(result, triads.getTotal());
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#listTopPagesEntryPoints(com.blandware.atleap.common.util.QueryInfo)
     */
    public PartialCollection listTopPagesEntryPoints(QueryInfo queryInfo) {
        PartialCollection triads = statisticsDAO.listTopPagesEntryPoints(queryInfo);
        List result = new ArrayList(triads.size());
        for (Iterator i = triads.iterator(); i.hasNext(); ) {
            Object[] triad = (Object[]) i.next();
            Page page = (Page) triad[0];
            Integer total = (Integer) triad[1];
            Integer unique = (Integer) triad[2];
            result.add(new ObjectCountInfo(page, total, unique));
        }
        return new PartialCollection(result, triads.getTotal());
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#listTopOtherEntryPoints(com.blandware.atleap.common.util.QueryInfo)
     */
    public PartialCollection listTopOtherEntryPoints(QueryInfo queryInfo) {
        PartialCollection triads = statisticsDAO.listTopOtherEntryPoints(queryInfo);
        List result = new ArrayList(triads.size());
        for (Iterator i = triads.iterator(); i.hasNext(); ) {
            Object[] triad = (Object[]) i.next();
            String url = (String) triad[0];
            Integer total = (Integer) triad[1];
            Integer unique = (Integer) triad[2];
            result.add(new ObjectCountInfo(url, total, unique));
        }
        return new PartialCollection(result, triads.getTotal());
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#listTopPagesExitPoints(com.blandware.atleap.common.util.QueryInfo)
     */
    public PartialCollection listTopPagesExitPoints(QueryInfo queryInfo) {
        PartialCollection triads = statisticsDAO.listTopPagesExitPoints(queryInfo);
        List result = new ArrayList(triads.size());
        for (Iterator i = triads.iterator(); i.hasNext(); ) {
            Object[] triad = (Object[]) i.next();
            Page page = (Page) triad[0];
            Integer total = (Integer) triad[1];
            Integer unique = (Integer) triad[2];
            result.add(new ObjectCountInfo(page, total, unique));
        }
        return new PartialCollection(result, triads.getTotal());
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#listTopOtherExitPoints(com.blandware.atleap.common.util.QueryInfo)
     */
    public PartialCollection listTopOtherExitPoints(QueryInfo queryInfo) {
        PartialCollection triads = statisticsDAO.listTopOtherExitPoints(queryInfo);
        List result = new ArrayList(triads.size());
        for (Iterator i = triads.iterator(); i.hasNext(); ) {
            Object[] triad = (Object[]) i.next();
            String url = (String) triad[0];
            Integer total = (Integer) triad[1];
            Integer unique = (Integer) triad[2];
            result.add(new ObjectCountInfo(url, total, unique));
        }
        return new PartialCollection(result, triads.getTotal());
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#listTopSearchEngines(com.blandware.atleap.common.util.QueryInfo)
     */
    public PartialCollection listTopSearchEngines(QueryInfo queryInfo) {
        PartialCollection triads = statisticsDAO.listTopSearchEngines(queryInfo);
        List result = new ArrayList(triads.size());
        for (Iterator i = triads.iterator(); i.hasNext(); ) {
            Object[] triad = (Object[]) i.next();
            Integer searchEngineId = (Integer) triad[0];
            Integer total = (Integer) triad[1];
            Integer unique = (Integer) triad[2];
            SearchEngineDescription description = visitorSoftwareUtil.getSearchEngineDescription(searchEngineId);
            result.add(new ObjectCountInfo(description, total, unique));
        }
        return new PartialCollection(result, triads.getTotal());
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#listTopSearchPhrases(com.blandware.atleap.common.util.QueryInfo)
     */
    public PartialCollection listTopSearchPhrases(QueryInfo queryInfo) {
        PartialCollection triads = statisticsDAO.listTopSearchPhrases(queryInfo);
        List result = new ArrayList(triads.size());
        for (Iterator i = triads.iterator(); i.hasNext(); ) {
            Object[] triad = (Object[]) i.next();
            String searchPhrase = (String) triad[0];
            Integer total = (Integer) triad[1];
            Integer unique = (Integer) triad[2];
            result.add(new ObjectCountInfo(searchPhrase, total, unique));
        }
        return new PartialCollection(result, triads.getTotal());
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#listTopSearchWords(com.blandware.atleap.common.util.QueryInfo)
     */
    public PartialCollection listTopSearchWords(QueryInfo queryInfo) {
        return statisticsDAO.listTopSearchWords(queryInfo);
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#listTopScreenResolutions(com.blandware.atleap.common.util.QueryInfo)
     */
    public PartialCollection listTopScreenResolutions(QueryInfo queryInfo) {
        PartialCollection tuples = statisticsDAO.listTopScreenResolutions(queryInfo);
        List result = new ArrayList(tuples.size());
        for (Iterator i = tuples.iterator(); i.hasNext(); ) {
            Object[] tuple = (Object[]) i.next();
            Integer screenWidth = (Integer) tuple[0];
            Integer screenHeight = (Integer) tuple[1];
            Integer total = (Integer) tuple[2];
            Integer unique = (Integer) tuple[3];
            String screenInfo;
            if (screenWidth == null || screenHeight == null) {
                screenInfo = "";
            } else {
                screenInfo = screenWidth.toString() + "x" + screenHeight;
            }
            result.add(new ObjectCountInfo(screenInfo, total, unique));
        }
        return new PartialCollection(result, tuples.getTotal());
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#listTopColorDepths(com.blandware.atleap.common.util.QueryInfo)
     */
    public PartialCollection listTopColorDepths(QueryInfo queryInfo) {
        PartialCollection tuples = statisticsDAO.listTopColorDepths(queryInfo);
        List result = new ArrayList(tuples.size());
        for (Iterator i = tuples.iterator(); i.hasNext(); ) {
            Object[] tuple = (Object[]) i.next();
            Integer colorDepth = (Integer) tuple[0];
            Integer total = (Integer) tuple[1];
            Integer unique = (Integer) tuple[2];
            result.add(new ObjectCountInfo(colorDepth, total, unique));
        }
        return new PartialCollection(result, tuples.getTotal());
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#listTopInternalSearchPhrases(com.blandware.atleap.common.util.QueryInfo)
     */
    public PartialCollection listTopInternalSearchPhrases(QueryInfo queryInfo) {
        PartialCollection tuples = statisticsDAO.listTopInternalSearchPhrases(queryInfo);
        List result = new ArrayList(tuples.size());
        for (Iterator i = tuples.iterator(); i.hasNext(); ) {
            Object[] tuple = (Object[]) i.next();
            String searchPhrase = (String) tuple[0];
            Integer total = (Integer) tuple[1];
            result.add(new ObjectCountInfo(searchPhrase, total, new Integer(0)));
        }
        return new PartialCollection(result, tuples.getTotal());
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#listTopInternalSearchWords(com.blandware.atleap.common.util.QueryInfo)
     */
    public PartialCollection listTopInternalSearchWords(QueryInfo queryInfo) {
        return statisticsDAO.listTopInternalSearchWords(queryInfo);
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#listVisitors(com.blandware.atleap.common.util.QueryInfo)
     */
    public PartialCollection listVisitors(QueryInfo queryInfo) {
        PartialCollection visitors = statisticsDAO.listVisitors(queryInfo);
        for (Iterator i = visitors.iterator(); i.hasNext(); ) {
            Visitor visitor = (Visitor) i.next();
            BrowserDescription browserDescription = visitorSoftwareUtil.getBrowserDescription(visitor.getBrowserId());
            OSDescription osDescription = visitorSoftwareUtil.getOSDescription(visitor.getOsId());
            visitor.setUserAgent(new UserAgent(browserDescription, osDescription));
        }
        return visitors;
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#listVisitorSessions(com.blandware.atleap.common.util.QueryInfo)
     */
    public PartialCollection listVisitorSessions(QueryInfo queryInfo) {
        return statisticsDAO.listVisitorSessions(queryInfo);
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#listVisits(com.blandware.atleap.common.util.QueryInfo)
     */
    public PartialCollection listVisits(QueryInfo queryInfo) {
        PartialCollection collection = statisticsDAO.listVisits(queryInfo);
        for (Iterator i = collection.iterator(); i.hasNext(); ) {
            Visit visit = (Visit) i.next();
            UserAgent ua = new UserAgent(visitorSoftwareUtil.getBrowserDescription(visit.getBrowserId()),
                                         visitorSoftwareUtil.getOSDescription(visit.getOsId()),
                                         visitorSoftwareUtil.getRobotDescription(visit.getRobotId()));
            visit.setUserAgent(ua);
        }
        return collection;
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#getTotalsMonthly()
     */
    public List getTotalsMonthly() {
        return statisticsDAO.getTotalsMonthly();
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#getTotalsDaily(Integer, Integer)
     */
    public List getTotalsDaily(Integer year, Integer month) {
        return statisticsDAO.getTotalsDaily(year, month);
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#getTotalsHourly(Integer, Integer, Integer)
     */
    public List getTotalsHourly(Integer year, Integer month, Integer day) {
        return statisticsDAO.getTotalsHourly(year, month, day);
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#retrieveVisit(Long)
     */
    public Visit retrieveVisit(Long visitId) {
        Visit visit = statisticsDAO.retrieveVisit(visitId);
        if (visit != null) {
            UserAgent ua = new UserAgent(visitorSoftwareUtil.getBrowserDescription(visit.getBrowserId()),
                                         visitorSoftwareUtil.getOSDescription(visit.getOsId()),
                                         visitorSoftwareUtil.getRobotDescription(visit.getRobotId()));
            visit.setUserAgent(ua);
        }
        return visit;
    }

    /**
     * @see com.blandware.atleap.service.core.StatisticsManager#createSearchCase(com.blandware.atleap.model.core.statistics.SearchCase)
     */
    public Long createSearchCase(SearchCase searchCase) {
        return statisticsDAO.createSearchCase(searchCase);
    }

    /**
     * Converts raw data to Visit instance, interpreting raw values.
     *
     * @param data raw data to convert
     * @return Visit instance
     */
    protected Visit convertRawDataToVisit(RawData data) {
        Visit visit = new Visit();

        // Point of visit
        String url = data.getUrl();
        if (GenericValidator.isBlankOrNull(url)) {
            url = null;
        }

        Page page = null;
        ContentResource contentResource = null;
        if (data.getPageUri() != null) {
            page = pageDAO.findPageByUri(data.getPageUri());
        }
        if (data.getContentResourceUri() != null) {
            contentResource = contentResourceDAO.findContentResourceByUri(data.getContentResourceUri());
        }

        visit.setUrl(url);
        visit.setPage(page);
        visit.setContentResource(contentResource);

        // Referrer URL and domain
        String referrerUrl = data.getReferrerUrl();
        String referrerDomain = data.getReferrerDomain();
        if (GenericValidator.isBlankOrNull(referrerUrl)) {
            referrerUrl = null;
        }
        visit.setReferrerUrl(referrerUrl);
        visit.setReferrerDomain(referrerDomain);

        // Session and user numbers
        visit.setSessionNumber(data.getSessionNumber());
        visit.setVisitorNumber(data.getVisitorNumber());

        // IP
        visit.setIp(data.getIp());

        // User agent
        String userAgentString = data.getUserAgent();
        UserAgent userAgent = visitorSoftwareUtil.detectUserAgent(userAgentString);
        BrowserDescription browserDescription = userAgent.getBrowserDescription();
        OSDescription osDescription = userAgent.getOsDescription();
        RobotDescription robotDescription = userAgent.getRobotDescription();
        Integer browserId = browserDescription == null
                ? null : browserDescription.getId();
        Integer osId = osDescription == null ? null : osDescription.getId();
        Integer robotId = robotDescription == null
                ? null : robotDescription.getId();
        visit.setBrowserId(browserId);
        visit.setOsId(osId);
        visit.setRobotId(robotId);

        // Search engine
        String searchPhrase = null;
        Integer searchEngineId = visitorSoftwareUtil.detectSearchEngineId(referrerDomain);
        if (Constants.UNKNOWN_SOFTWARE_ID.equals(searchEngineId)) {
            searchEngineId = null;
        } else {
            searchPhrase = visitorSoftwareUtil.extractSearchPhrase(data.getReferrerQueryParameters(), searchEngineId);
        }
        visit.setSearchEngineId(searchEngineId);
        visit.setSearchPhrase(searchPhrase);

        // User
        User user = null;
        String userName = data.getUserName();
        if (userName != null) {
            user = userDAO.retrieveUser(userName);
        }
        visit.setUser(user);

        // Screen info
        Integer width = null;
        Integer height = null;
        Integer colorDepth = null;
        if (data.getScreenInfo() != null) {
            List screenInfo = ConvertUtil.convertStringToList(data.getScreenInfo(), "x", true);
            if (screenInfo.size() == 3) {
                try {
                    width = Integer.valueOf((String) screenInfo.get(0));
                    height = Integer.valueOf((String) screenInfo.get(1));
                    colorDepth = Integer.valueOf((String) screenInfo.get(2));
                } catch (NumberFormatException e) {
                    width = null;
                    height = null;
                    colorDepth = null;
                }
            }
        }
        visit.setScreenWidth(width);
        visit.setScreenHeight(height);
        visit.setColorDepth(colorDepth);

        // Date
        visit.setDate(data.getDate());

        return visit;
    }
}
